<?php
	/*
		SiLL Version : {Development Version}
		File Revision : {Development Revision}

		SiLL
		Copyright (C) 2013 Matt Stypa

		This program is free software; you can redistribute it and/or
		modify it under the terms of the GNU General Public License
		as published by the Free Software Foundation; either version 2
		of the License, or (at your option) any later version.

		This program is distributed in the hope that it will be useful,
		but WITHOUT ANY WARRANTY; without even the implied warranty of
		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
		GNU General Public License for more details.

		You should have received a copy of the GNU General Public License
		along with this program; if not, write to the Free Software
		Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
	*/

	/**
	 * An instance of this Class is automatically created when <b>SiLL</b> is loaded. It is then assigned to a global variable $sill. There is no need to create any more instances of this Class.
	 * 
	 * @version {Development Version}
	 * @package <b>SiLL</b>
	 * @author Matt Stypa
	 */
	class Sill {

		/**
		 * Configuration.
		 * 
		 * @var array
		 */
		private $_config;

		/**
		 * Routes.
		 * 
		 * @var array
		 */
		private $_routes;
		
		/**
		 * PDO object.
		 * 
		 * @var PDO
		 */
		private $_PDO;
		
		/**
		 * Matches made against the route.
		 * 
		 * @var array
		 */
		private $_arguments;

		/**
		 * <b>SiLL</b> version.
		 * 
		 * @var string
		 */
		private $_version;

		/**
		 * Initilized modules by directory.
		 * 
		 * @var (mixed)
		 */
		public $modules;

		/**
		 * Initilized modules by Class name.
		 * 
		 * @var (mixed)
		 */
		public $modules_by_class;

		/**
		 * Constructor.
		 */
		public function __construct($config) {

			$this->_config = $config;
			$this->_routes = array();
			$this->_data = FALSE;
			$this->_debug = FALSE;
			$this->_arguments = array();
			$this->_version = '{Development Version}';
			$this->modules = new stdClass();
			$this->modules_by_class = new stdClass();

			if ($this->_config['DEBUG'])
				set_error_handler("Sill::error_handler");
			else
				error_reporting(0);

		}

		/**
		 * Desturctor.
		 */
		public function __destruct() {

			unset($this->_config);
			unset($this->_routes);
			unset($this->_data);
			unset($this->_debug);
			unset($this->_arguments);
			unset($this->_version);
			unset($this->modules);
			unset($this->modules_by_class);
			
		}

		/**
		 * Initializes <b>SiLL</b>
		 * 
		 * @internal
		 */
		public function init() {
		
			// Get requested URL
			$requested_path = parse_url((!empty($_SERVER["HTTPS"]) ? 'https://' : 'http://') . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]);
			$requested_path = $requested_path['scheme'] . '://' . $requested_path['host'] . (isset($requested_path['path']) ? $requested_path['path'] : '');
			
			if (strpos($requested_path, $this->URL()) == 0)
				$requested_path =  substr($requested_path, strlen($this->URL()));

			// If requested URL is the same as base URL there is no need to check routes
			if (!$requested_path) {
			
				$this->load('index.php');
				exit;

			}

			// Look at the routes
			foreach($this->_routes as $route => $file) {

				// Check if the requested URL matches the rule and save the URL variables
				if (preg_match($route, $requested_path, $this->_arguments)) {
				
					// Check if the theme file exists and include it
					if ($this->load($file) || $this->_config['DEBUG'])
						exit;

				}

			}

			// Redirect back to base URL if no match has been found
			header('Location: ' . $this->URL());

		}
		
		/**
		 * Loads requested file from the '/site/' directory if it exists.
		 * 
		 * @param string $file Relative path of the file.
		 * @return boolean TRUE if file exists, FALSE otherwise.
		 */
		public function load($file) {

			if (!file_exists('site/' . $file)) {

				trigger_error('File does not exist. ( ' . $file . ' )', E_USER_NOTICE);
				return FALSE;

			}

			require('site/' . $file);
			return TRUE;

		}		

		/**
		 * Registers a route.
		 * 
		 * @param string $route Regular Expression against which the requested URLs are matched.
		 * @param string $file Relative path of the file the request will be directed to.
		 */
		public function route($route, $file) {

			$this->_routes[$route] = $file;

		}		

		/**
		 * Loads and initializes a module.
		 * 
		 * @param string $module Name of module's directory.
		 * @param mixed [...] (optional) Additional arguments that will be passed to the module's constructor.
		 * @return mixed Instance of the module.
		 */		 
		public function module($module) {
		
			if (!file_exists('site/modules/' . $module . '/functions.php')) {

				trigger_error('Module does not exist. ( ' . $module . ' )', E_USER_ERROR);
				return FALSE;

			}

			$module_class = FALSE;
			require('site/modules/' . $module . '/functions.php');
			
			if (!class_exists($module_class)) {

				trigger_error('Module is invalid. ( ' . $module . ' )', E_USER_ERROR);
				return FALSE;

			}
			
			$args = func_get_args();
			unset($args[0]);
			$args = array_values($args);

			$reflection = new ReflectionClass($module_class);

			$this->modules->{$module} = $reflection->newInstanceArgs($args);
			$this->modules_by_class->{$module_class} = $this->modules->{$module};
			
			return TRUE;
		}
		
		/**
		 * Gets the absolute URL.
		 * 
		 * @param string $URL (optional) relative URL to generate absolute URL from.
		 * @return string Abolute URL.
		 */
		public function URL($URL = '') {

			if (!$this->_config['SILL_BASE_URL'])
				$URL = (!empty($_SERVER["HTTPS"]) ? 'https://' : 'http://') . $_SERVER["HTTP_HOST"] . '/' . $URL;
			elseif ($URL && substr($this->_config['SILL_BASE_URL'], -1) != '/')
				$URL = '/' . $URL;
		
			return $this->_config['SILL_BASE_URL'] . $URL;

		}	

		/**
		 * Gets arguments captured by the route.
		 * 
		 * @return array Matches made against the rewrite rule.
		 */
		public function arguments() {

			return $this->_arguments;

		}
		
		/**
		 * Returns passed variable if it exists.
		 *
		 * @param mixed $v Variable to verify.
		 * @param mixed [...] (optional) Additional arguments specifying subsequent keys of the array or attributes of the object.
		 * @return mixed Passed variable if it exists, FALSE otherwise.
		 */
		public function v(&$v) {
		
			if (!isset($v))
				return FALSE;

			$r=$v;
			
			for ($i=1; $i<func_num_args(); $i++)
				if (is_object($v) && isset($r->{func_get_arg($i)}))
					$r=$r->{func_get_arg($i)};
				elseif (is_array($v) && isset($r[func_get_arg($i)]))
					$r=$r[func_get_arg($i)];
				else
					return FALSE;
				
			return $r;
		
		}

		/**
		 * Gets a PDO object.
		 * 
		 * @return PDO
		 */
		public function PDO() {

			if (!$this->_PDO) {
			
				try {
				
					$this->_PDO = new PDO('mysql:dbname=' . $this->_config['SILL_DB_NAME'] . ';host=' . $this->_config['SILL_DB_HOST'], $this->_config['SILL_DB_USER'], $this->_config['SILL_DB_PASS']);
					
				} catch (PDOException $e) {
					
					trigger_error('Unable to connect to the Database.', E_USER_ERROR);
					exit;
					
				}
		
			}
		
			return $this->_PDO;

		}
		
		/**
		 * Gets <b>SiLL</b> version.
		 * 
		 * @return string SiLL version.
		 */
		public function version() {

			return $this->_version;

		}

		/**
		 * <b>SiLL</b> error handler. The parameters are specified by PHP set_error_handler function.
		 */
		public static function error_handler($error_no, $error_message, $error_file, $error_line) {
		
			$backtrace = debug_backtrace();
			$core_path = dirname(__FILE__);
			
			// Find first backtrace step outside of core
			foreach ($backtrace as $backtrace_step)
				if (isset($backtrace_step['file']) && substr($backtrace_step['file'], 0, strlen($core_path)) != $core_path)
					break;
					
			$backtrace = $backtrace_step;
			
			if ($error_no == E_USER_NOTICE) {
			
				$type = 'warning';
				$color = 'c09853';
				$background_color = 'fcf8e3';
				$border_color = 'fbeed5';
			
			} else {
			
				$type = 'error';
				$color = 'b94a48';
				$background_color = 'f2dede';
				$border_color = 'eed3d7';

			}
			
			?>
			
				<div style="margin:10px 0;color:#<?php echo $color; ?>;background-color:#<?php echo $background_color; ?>;padding:8px 14px;border:1px solid #<?php echo $border_color; ?>;border-radius:4px;display:block;font-family:'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:14px;line-height:20px;">
					<strong style="font-size:18px;">SiLL <?php echo $type; ?>!</strong><br/><br/>
					<?php echo $error_message; ?><br/><br/>
					in <?php echo $backtrace['file']; ?> on Line <?php echo $backtrace['line']; ?>
				</div>
			
			<?php
			
			if ($type == 'error')
				exit;
			
			return TRUE;
			
		}
		
	}

?>